package com.transaction.manage.service.dayEnd;

import com.transaction.common.util.ConstUtil;
import com.transaction.common.util.DataUtil;
import com.transaction.common.util.DateUtil;
import com.transaction.manage.localMapper.*;
import com.transaction.manage.model.dto.*;
import com.transaction.manage.service.BusinessHandleService;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 日终日启处理校验
 */
@Component
public class HandleDataCheck {

    private static Logger LOGGER = LoggerFactory.getLogger(HandleDataCheck.class);

    @Autowired
    private MarketDateMapper marketDateMapper;

    @Autowired
    private FlowLogMapper flowLogMapper;

    @Autowired
    private FundOpenDateMapper fundOpenDateMapper;

    @Autowired
    private MarketMapper marketMapper;

    @Autowired
    private InterfaceFieldMapper interfaceFieldMapper;

    @Autowired
    private AccountStatusMapper accountStatusMapper;

    @Autowired
    private AccountApplyMapper accountApplyMapper;

    @Autowired
    private TransApplyMapper transApplyMapper;

    @Autowired
    private BusinessHandleService businessHandleService;

    @Autowired
    private FundInfoMapper fundInfoMapper;

    @Autowired
    private FundTransRateMapper fundTransRateMapper;

    @Autowired
    private FundYieldMapper fundYieldMapper;

    @Autowired
    private SequenceMapper sequenceMapper;


    /**
     * 校验渠道处理日期是否是交易日
     *
     * @param channelCode 渠道编号
     * @param handleDate  处理日期
     * @return
     */
    public boolean checkHandleDateIsWorkDate(String channelCode, String handleDate) {
        int count = marketDateMapper.checkHandleDateIsWorkDate(channelCode, handleDate);
        return count > 0 ? true : false;
    }

    /**
     * 校验渠道是否正在处理当中，防止数据错乱
     *
     * @param flowCode    流程编码
     * @param channelCode 渠道编码
     * @param handleDate  处理日期
     * @return
     */
    public boolean checkFlowIsProcess(String flowCode, String channelCode, String handleDate) {
        int count = flowLogMapper.checkFlowIsProcess(flowCode, channelCode, handleDate);
        return count > 0 ? true : false;
    }

    /**
     * 判断当前日期是否是丰利类产品的结转日
     *
     * @param marketCode 市场编码
     * @param handleDate 操作日期
     * @return
     */
    public boolean checkDayIsFundType1CarryOver(String marketCode, String handleDate) {
        String day = "15";//TODO
        int count = fundOpenDateMapper.checkDayIsCarryOverDay(marketCode, handleDate, day, null);
        return count > 0 ? true : false;
    }

    /**
     * 判断当前日期是否是红七类产品的结转日
     *
     * @param marketCode 市场编码
     * @param handleDate 操作日期
     * @return
     */
    public boolean checkDayIsFundType2CarryOver(String marketCode, String handleDate) {
        String day = "15";//TODO
        String containsMonth = "'03','06','09','12'";//TODO
        int count = fundOpenDateMapper.checkDayIsCarryOverDay(marketCode, handleDate, day, containsMonth);
        return count > 0 ? true : false;
    }

    /**
     * 判断当前生成的行情产品是否都有对应的TA产品
     *
     * @param channelCode 渠道编码
     * @param handleDate  操作日期
     * @return
     */
    public List<LinkedHashMap<String, Object>> checkMarketFundNotMappingTaFund(String channelCode, String handleDate) {
        return marketMapper.checkMarketFundNotMappingTaFund(channelCode, handleDate);
    }


    /**
     * 验证当前流程的当前渠道的当前处理日期当前步骤是否是最新错误节点
     *
     * @param flowCode    流程编码
     * @param channelCode 渠道编码
     * @param handleDate  处理日期
     * @param stepCode    步骤编码
     * @return
     */
    public boolean checkStepIsNewestErrorStep(String flowCode, String channelCode, String handleDate, Integer stepCode) {
        int count = flowLogMapper.checkStepIsNewestErrorStep(flowCode, channelCode, handleDate, stepCode);
        return count > 0 ? true : false;
    }

    /**
     * 查询重复的appsheetserialno
     *
     * @param tableName   表名
     * @param channelCode 渠道编码
     * @param handleDate  处理日期
     * @return
     */
    public List<String> checkSameAppSheetSerialNo(String tableName, String channelCode, String handleDate) {
        return flowLogMapper.checkSameAppSheetSerialNo(tableName, channelCode, handleDate);
    }

    /**
     * 处理数据基础校验
     *
     * @param data       数据集
     * @param columnList 字段集合
     * @return
     */
    public boolean dataBaseCheck(LinkedHashMap<String, Object> data, List<Map<String, Object>> columnList) {
        boolean flag = false;

        /**
         * 1、必填
         * 2、类型
         * 3、字典
         * 4、规则
         */
        for (Map<String, Object> v : columnList) {
            String colEnName = MapUtils.getString(v, "COLENNAME");
            Integer colRequired = MapUtils.getInteger(v, "COLREQUIRED");
            String colType = MapUtils.getString(v, "COLTYPE");
            String colDict = MapUtils.getString(v, "COLDICT");
            String colRule = MapUtils.getString(v, "COLRULE");

            String value = MapUtils.getString(data, colEnName);
            //BigDecimal类型值为0时会出现0E-X,需转换 TODO
            if (StringUtils.isNotEmpty(value) && value.contains("0E-")) {
                Integer x = Integer.valueOf(value.substring(value.indexOf("0E-") + 3, value.length()));
                value = DataUtil.fillingDecimalPlaces("0", x - 1);
            }

            //必填
            boolean requiredFlag = checkColumnRequiredValid(value, colRequired);
            if (!requiredFlag) {
                LOGGER.error("字段:【{}】,该字段为必填项,值为空,请检查。", colEnName);
                data.put("ERRORMSG", MapUtils.getString(data, "ERRORMSG", "")
                        + "字段:" + colEnName + ",该字段为必填项,值为空,请检查。");
                flag = true;
                continue;
            }

            //类型
            boolean typeFlag = checkColumnTypeValid(colType, value);
            if (!typeFlag) {
                LOGGER.error("字段:【{}】,该字段值:【{}】,与其类型:【{}】不符,请检查。", colEnName, value, colType);
                data.put("ERRORMSG", MapUtils.getString(data, "ERRORMSG", "")
                        + "字段:" + colEnName + ",该字段值:" + value + "与其类型:" + colType + "不符,请检查。");
                flag = true;
                continue;
            }

            //字典
            boolean dictFlag = checkColumnDictValid(value, colDict);
            if (!dictFlag) {
                LOGGER.error("字段:【{}】,该字段值:【{}】,不在字典:【{}】范围内,请检查。", colEnName, value, colDict);
                data.put("ERRORMSG", MapUtils.getString(data, "ERRORMSG", "")
                        + "字段:" + colEnName + ",该字段值:" + value + ",不在字典:" + colDict + "范围内,请检查。");
                flag = true;
                continue;
            }

            //规则
            boolean ruleFlag = checkColumnRuleValid(value, colRule);
            if (!ruleFlag) {
                LOGGER.error("字段:【{}】,该字段值:【{}】,不符合规则:【{}】,请检查。", colEnName, value, colRule);
                data.put("ERRORMSG", MapUtils.getString(data, "ERRORMSG", "")
                        + "字段:" + colEnName + ",该字段值:" + value + ",不符合规则:" + colRule + ",请检查。");
                flag = true;
                continue;
            }
        }

        return flag;

    }

    /**
     * 校验字段规则
     *
     * @param value   值
     * @param colRule 字段规则
     * @return
     */
    private boolean checkColumnRuleValid(String value, String colRule) {
        if (StringUtils.isBlank(value) || StringUtils.isBlank(colRule)) return true;
        return Pattern.matches(colRule, value);
    }

    /**
     * 校验字段字典值
     *
     * @param value   值
     * @param colDict 字典
     * @return
     */
    private boolean checkColumnDictValid(String value, String colDict) {
        if (StringUtils.isBlank(value) || StringUtils.isBlank(colDict)) return true;
        if (!colDict.contains(value)) {
            return false;
        }
        return true;
    }


    /**
     * 校验字段类型
     *
     * @param colType 字段类型
     * @param value   值
     * @return
     */
    private boolean checkColumnTypeValid(String colType, String value) {
        if (StringUtils.isBlank(value)) return true;
        if ("A".equals(colType) || "N".equals(colType)) {
            return checkColumnRuleValid(value.replace(".", ""), "[0-9]*");
        }
        return true;
    }

    /**
     * 校验必填
     *
     * @param value       值
     * @param colRequired 是否必填
     * @return
     */
    private boolean checkColumnRequiredValid(String value, Integer colRequired) {
        if (StringUtils.isBlank(value) && colRequired == 1) {
            return false;
        }
        return true;
    }

    /**
     * 数据基础校验公共方法
     *
     * @param dataList        数据集合
     * @param interfaceList   字段数据集
     * @param checkList       校验错误集合
     * @param supportBusiness 支持的业务类型
     * @param channelCode     渠道编码
     * @param handleDate      处理日期
     * @param fileType        文件类型
     * @param versionCode     版本号
     * @param stepCode        步骤编码
     * @return
     */
    public boolean baseDataCheck(List<LinkedHashMap<String, Object>> dataList,
                                 List<Map<String, Object>> interfaceList,
                                 List<LinkedHashMap<String, Object>> checkList,
                                 String supportBusiness,
                                 String channelCode, String handleDate,
                                 String fileType, String versionCode,
                                 Integer stepCode) {
        boolean flag = false;


        for (LinkedHashMap<String, Object> map : dataList) {

            String taSerialNo = DateUtil.formatDate(new Date(),DateUtil.FORMAT_STR_TIME14) + sequenceMapper.getNextValue("taSerialNo");
            String businessCode = MapUtils.getString(map, "BUSINESSCODE");
            String appSheetSerialNo = MapUtils.getString(map, "APPSHEETSERIALNO");

            //默认给taSerialNo赋值,账户和交易
            if (ConstUtil.FILE_TYPE_01.equals(fileType) || ConstUtil.FILE_TYPE_03.equals(fileType) || ConstUtil.FILE_TYPE_R1.equals(fileType)) {
                map.put("TASERIALNO", taSerialNo);
            }

            //默认成功
            map.put("FLOWSTEP", ",stepCode_" + stepCode + ":success");


            if (StringUtils.isNotBlank(supportBusiness)) {
                //判断是否支持的业务类型
                if (StringUtils.isBlank(businessCode) || !supportBusiness.contains(businessCode)) {
                    LOGGER.error("渠道:【{}】,日期:【{}】,AppSheetSerialNo:【{}】,业务编码为:【{}】,不在系统支持的范围内。",
                            channelCode, handleDate, appSheetSerialNo, businessCode);
                    map.put("ERRORMSG", MapUtils.getString(map, "ERRORMSG", "") + businessCode + "该业务编码不在系统支持的范围内。");
                    flag = true;
                    checkList.add(map);
                    map.put("FLOWSTEP", ",stepCode_" + stepCode + ":failed");
                    map.put("CHECKSTATUS", 0);
                    continue;
                }
            }

            //查询接口
            if (CollectionUtils.isEmpty(interfaceList)) {
                String interfaceCode = fileType + businessCode + versionCode;
                interfaceList = interfaceFieldMapper.selectInterfaceForMap(interfaceCode);
            }
            if (CollectionUtils.isEmpty(interfaceList)) {
                LOGGER.error("渠道:【{}】,日期:【{}】,AppSheetSerialNo:【{}】,业务编码为:【{}】,没有查询到相关接口配置。",
                        channelCode, handleDate, appSheetSerialNo, businessCode);
                map.put("ERRORMSG", MapUtils.getString(map, "ERRORMSG", "") + businessCode + "该业务编码没有查询到相关接口配置。");
                flag = true;
                checkList.add(map);
                map.put("FLOWSTEP", ",stepCode_" + stepCode + ":failed");
                map.put("CHECKSTATUS", 0);
                continue;
            }

            //基础校验
            boolean retFlag = dataBaseCheck(map, interfaceList);
            if (retFlag) {
                flag = true;
                checkList.add(map);
                map.put("FLOWSTEP", ",stepCode_" + stepCode + ":failed");
                map.put("CHECKSTATUS", 0);
            }
        }

        return flag;
    }

    /**
     * 账户申请业务校验
     *
     * @param channelCode  渠道编码
     * @param handleDate   处理日期
     * @param businessCode 业务编码
     * @param account      账户数据
     * @param stepCode     步骤编码
     * @param checkList    校验错误集合
     * @return
     */
    public boolean accountBusinessCheck(String channelCode, String handleDate, String businessCode,
                                        LinkedHashMap<String, Object> account, Integer stepCode,
                                        List<LinkedHashMap<String, Object>> checkList) {
        boolean checkFlag = false;

        switch (businessCode) {
            case ConstUtil.OPEN_ACCOUNT:
                boolean openFlag = accountOpenBusinessCheck(channelCode, handleDate, account, stepCode, checkList);
                if (openFlag) {
                    checkFlag = true;
                }
                break;
            case ConstUtil.UPDATE_ACCOUNT:
                boolean updateFlag = accountUpdateBusinessCheck(channelCode, handleDate, account, stepCode, checkList);
                if (updateFlag) {
                    checkFlag = true;
                }
                break;
            case ConstUtil.CLOSE_ACCOUNT:
                boolean closeFlag = accountCloseBusinessCheck(channelCode, handleDate, account, stepCode, checkList);
                if (closeFlag) {
                    checkFlag = true;
                }
                break;
            case ConstUtil.CANCEL_ACCOUNT:
                boolean cancelFlag = accountCloseBusinessCheck(channelCode, handleDate, account, stepCode, checkList);
                if (cancelFlag) {
                    checkFlag = true;
                }
                break;
        }

        return checkFlag;
    }

    /**
     * 3、销户
     * 3.1、查看销户是否有开户账户；无 （销户失败）
     * 3.2、查看该客户对应的有效合同的持仓是否为0,为0,把所有有效合同销毁,修改账户状态;否，销户失败（失败）
     *
     * @param channelCode 渠道编码
     * @param handleDate  处理日期
     * @param account     开户数据
     * @param stepCode    步骤编码
     * @param checkList   校验错误结果
     * @return
     */
    private boolean accountCloseBusinessCheck(String channelCode, String handleDate, LinkedHashMap<String, Object> account, Integer stepCode, List<LinkedHashMap<String, Object>> checkList) {
        boolean flag = false;

        //根据transactionAccountId查询本地账户状态表数据
        String transactionAccountId = MapUtils.getString(account, "TRANSACTIONACCOUNTID");
        AccountStatus accountStatus = accountStatusMapper.selectOne(new AccountStatus(channelCode, transactionAccountId));

        if (accountStatus == null) {
            LOGGER.error("渠道:【{}】,处理日期:【{}】,销户账申请投资人基金交易账号:【{}】,未开户,不能销户,请检查。", channelCode, handleDate, transactionAccountId);
            account.put("FLOWSTEP", ",stepCode_" + stepCode + ":failed");
            account.put("CHECKSTATUS", 0);
            account.put("SENDTAFLAG", -1);
            account.put("ERRORMSG", "该销户账申请投资人基金交易账号:" + transactionAccountId + ",未开户,不能销户,请检查。");
            checkList.add(account);
            flag = true;
        } else {
            //查看该账户状态,开户状态修改状态表；销户状态，不做操作
            Integer status = accountStatus.getStatus();
            if ("1".equals(status)) {//开户
                //查询持仓  TODO 默认都为0
                BigDecimal shares = new BigDecimal(0);
                if (BigDecimal.ZERO.compareTo(shares) == 0) {
                    //修改状态
                    accountStatusMapper.updateStatusByChannelCodeAndTransactionAccountId(channelCode, transactionAccountId, 0);
                    //把所有的有效合同修改为失效状态

                } else {
                    LOGGER.error("渠道:【{}】,处理日期:【{}】,销户账申请投资人基金交易账号:【{}】,还存在持仓份额:【{}】,不能销户,请检查。", channelCode, handleDate, transactionAccountId, shares);
                    account.put("FLOWSTEP", ",stepCode_" + stepCode + ":failed");
                    account.put("CHECKSTATUS", 0);
                    account.put("SENDTAFLAG", -1);
                    account.put("ERRORMSG", "该销户账申请投资人基金交易账号:" + transactionAccountId + ",还存在持仓份额:" + shares + ",不能销户,请检查。");
                    checkList.add(account);
                    flag = true;
                }
            }
        }
        return flag;
    }

    /**
     * 2、修改资料
     * 2.1 没有查到已开户数据,可能是今天的开户账户,如果都没有，失败。（失败）
     * 2.2 查看该账户状态,销户状态不允许修改资料
     * 2.3 如果4要素和transactionAccountId一致,则是修改其他信息
     * 2.4 如果4要素和transactionAccountId一致,则是修改4要素和其他信息
     *
     * @param channelCode 渠道编码
     * @param handleDate  处理日期
     * @param account     开户数据
     * @param stepCode    步骤编码
     * @param checkList   校验错误结果
     * @return
     */
    private boolean accountUpdateBusinessCheck(String channelCode, String handleDate, LinkedHashMap<String, Object> account, Integer stepCode, List<LinkedHashMap<String, Object>> checkList) {

        boolean flag = false;

        //根据transactionAccountId查询本地账户状态表数据
        String transactionAccountId = MapUtils.getString(account, "TRANSACTIONACCOUNTID");
        AccountStatus accountStatus = accountStatusMapper.selectOne(new AccountStatus(channelCode, transactionAccountId));

        if (accountStatus == null) {
            //查询当日的校验成功的开户数据
            List<AccountApply> accountApplyList = accountApplyMapper.select(new AccountApply(channelCode, handleDate, transactionAccountId, ConstUtil.OPEN_ACCOUNT, 1));
            if (CollectionUtils.isEmpty(accountApplyList)) {
                LOGGER.error("渠道:【{}】,处理日期:【{}】,修改账户申请投资人基金交易账号:【{}】,该账户未开户,不能直接修改账户信息,请检查。", channelCode, handleDate, transactionAccountId);
                account.put("FLOWSTEP", ",stepCode_" + stepCode + ":failed");
                account.put("CHECKSTATUS", 0);
                account.put("SENDTAFLAG", -1);
                account.put("ERRORMSG", "该修改资料投资人基金交易账号:" + transactionAccountId + ",未开户,不能直接修改账户信息,请检查。");
                checkList.add(account);
                flag = true;
            }
        } else {
            //查看该账户状态,销户状态不允许修改资料
            Integer status = accountStatus.getStatus();
            if ("0".equals(status)) {//销户
                LOGGER.error("渠道:【{}】,处理日期:【{}】,修改账户申请投资人基金交易账号:【{}】,该账户是【销户状态】,不能直接修改账户信息,请检查。", channelCode, handleDate, transactionAccountId);
                account.put("TAACCOUNTID", accountStatus.getTaAccountId());
                account.put("FLOWSTEP", ",stepCode_" + stepCode + ":failed");
                account.put("CHECKSTATUS", 0);
                account.put("SENDTAFLAG", -1);
                account.put("ERRORMSG", "该修改资料投资人基金交易账号:" + transactionAccountId + ",是【销户状态】,不能直接修改账户信息,请检查。");
                checkList.add(account);
                flag = true;
            } else if ("1".equals(status)) {//开户
                String individualOrInstitution = MapUtils.getString(account, "INDIVIDUALORINSTITUTION");
                String certificateType = MapUtils.getString(account, "CERTIFICATETYPE");
                String certificateNo = MapUtils.getString(account, "CERTIFICATENO");
                String investorName = MapUtils.getString(account, "INVESTORNAME");

                //比较4要素是否一致
                if (accountStatus.getIndividualOrInstitution().equals(individualOrInstitution) &&
                        accountStatus.getCertificateType().equals(certificateType) &&
                        accountStatus.getCertificateNo().equals(certificateNo) &&
                        accountStatus.getInvestorName().equals(investorName)) {
                    //修改其他账户要素
                    LOGGER.info("渠道:【{}】,处理日期:【{}】,修改账户申请投资人基金交易账号:【{}】,该账户修改普通账户信息。", channelCode, handleDate, transactionAccountId);
                } else {
                    //修改四要素,修改状态表
                    LOGGER.warn("渠道:【{}】,处理日期:【{}】,修改账户申请投资人基金交易账号:【{}】,该账户修改账户4要素信息。", channelCode, handleDate, transactionAccountId);
                    accountStatusMapper.update4ElementByChannelCodeAndTransactionAccountId(channelCode, handleDate, account);
                }
            }
        }
        return flag;
    }

    /**
     * 1、重复开户,已有开户的transactionAccountId
     * 1.1 如果4要素和transactionAccountId一致，直接返回，不用发送核心；
     * 判断账户状态是否是开户状态，如果不是，需要更新开户状态为开户
     * 1.2 如果4要素不一致，则该开户申请失败（失败）
     *
     * @param channelCode 渠道编码
     * @param handleDate  处理日期
     * @param account     开户数据
     * @param stepCode    步骤编码
     * @param checkList   校验错误结果
     * @return
     */
    private boolean accountOpenBusinessCheck(String channelCode, String handleDate,
                                             LinkedHashMap<String, Object> account,
                                             Integer stepCode, List<LinkedHashMap<String, Object>> checkList) {
        //是否有校验失败的
        boolean flag = false;

        String individualOrInstitution = MapUtils.getString(account, "INDIVIDUALORINSTITUTION");
        String certificateType = MapUtils.getString(account, "CERTIFICATETYPE");
        String certificateNo = MapUtils.getString(account, "CERTIFICATENO");
        String investorName = MapUtils.getString(account, "INVESTORNAME");

        //根据transactionAccountId查询本地账户状态表数据
        String transactionAccountId = MapUtils.getString(account, "TRANSACTIONACCOUNTID");
        AccountStatus accountStatus = accountStatusMapper.selectOne(new AccountStatus(channelCode, transactionAccountId));

        //已有开户的数据
        if (accountStatus != null) {
            //比较4要素是否一致
            if (accountStatus.getIndividualOrInstitution().equals(individualOrInstitution) &&
                    accountStatus.getCertificateType().equals(certificateType) &&
                    accountStatus.getCertificateNo().equals(certificateNo) &&
                    accountStatus.getInvestorName().equals(investorName)) {
                //已有账户重复开户,判断账户状态是否是开户状态，如果不是，需要更新开户状态为开户,把TaAccountId赋值给账户申请
                account.put("TAACCOUNTID", accountStatus.getTaAccountId());
                account.put("SENDTAFLAG", -1);
                //查看该账户状态,如果是销户状态
                Integer status = accountStatus.getStatus();
                if ("0".equals(status)) {
                    accountStatusMapper.updateStatusByChannelCodeAndTransactionAccountId(channelCode, transactionAccountId, 1);
                }
            } else {
                account.put("FLOWSTEP", ",stepCode_" + stepCode + ":failed");
                account.put("CHECKSTATUS", 0);
                account.put("SENDTAFLAG", -1);
                LOGGER.error("渠道:【{}】,处理日期:【{}】,开户申请投资人基金交易账号:【{}】,4要素：【{}】-【{}】-【{}】-【{}】," +
                        "与已开户的4要素不一致,请检查。", channelCode, handleDate, transactionAccountId, individualOrInstitution, certificateType, certificateNo, investorName);
                account.put("ERRORMSG", "改开户投资人基金交易账号:" + transactionAccountId + ",4要素：" + individualOrInstitution + "-" + certificateType + "-" + certificateNo + "-" + investorName + ",与已开户的4要素不一致,请检查。");
                checkList.add(account);
                flag = true;
            }
        }

        //新开户的申请直接返回
        return flag;
    }

    /**
     * 特殊校验--1、客户类型是否满足；2、证件类型是否满足； 3、新增必填字段是否满足 4、身份证开户和性别是否一致；5、开户申请是否有其对应的反洗钱数据
     *
     * @param channelInfo 渠道信息
     * @param handleDate  处理日期
     * @param checkList   校验错误信息
     * @return
     */
    public boolean accountSpecialBusinessCheck(ChannelInfo channelInfo, String handleDate,
                                               List<LinkedHashMap<String, Object>> checkList) {
        //是否有校验失败的数据
        boolean flag = false;

        String channelCode = channelInfo.getChannelCode();
        String versionCode = channelInfo.getVersionCode();
        String indiOrInst = "0,1";
        String indiOpenAccountType = channelInfo.getIndiOpenAccountType();
        String instOpenAccountType = channelInfo.getInstOpenAccountType();
        String requiredField = "address,post_Code";


        //1、客户类型是否满足
        List<LinkedHashMap<String, Object>> indiOrInstNotSupport = accountApplyMapper.indiOrInstNotSupportList(channelCode, handleDate, indiOrInst);
        if (CollectionUtils.isNotEmpty(indiOrInstNotSupport)) {
            businessHandleService.updateSerialNoAndCheckErrorList("account_apply", indiOrInstNotSupport);
            checkList.addAll(indiOrInstNotSupport);
            flag = true;
        }

        //2、证件类型是否满足
        List<LinkedHashMap<String, Object>> openAccountTypeNotSupport = accountApplyMapper.openAccountTypeNotSupportList(channelCode, handleDate, indiOpenAccountType, instOpenAccountType);
        if (CollectionUtils.isNotEmpty(openAccountTypeNotSupport)) {
            businessHandleService.updateSerialNoAndCheckErrorList("account_apply", openAccountTypeNotSupport);
            checkList.addAll(openAccountTypeNotSupport);
            flag = true;
        }

        //3、新增必填字段是否满足
        List<LinkedHashMap<String, Object>> requiredFieldIsEmpty = accountApplyMapper.requiredFieldIsEmptyList(channelCode, handleDate, requiredField.split(","));
        if (CollectionUtils.isNotEmpty(requiredFieldIsEmpty)) {
            businessHandleService.updateSerialNoAndCheckErrorList("account_apply", requiredFieldIsEmpty);
            checkList.addAll(requiredFieldIsEmpty);
            flag = true;
        }

        //4、身份证开户和性别是否一致
        List<LinkedHashMap<String, Object>> sexNotSameForCertificateNo = accountApplyMapper.sexNotSameForCertificateNoList(channelCode, handleDate);
        if (CollectionUtils.isNotEmpty(sexNotSameForCertificateNo)) {
            checkList.addAll(sexNotSameForCertificateNo);
            flag = true;
        }

        if (ConstUtil.VERSION_22.equals(versionCode)) {
            //5、开户申请是否有其对应的反洗钱数据  TODO
            List<LinkedHashMap<String, Object>> openAccountNotMappingToFxq = accountApplyMapper.openAccountNotMappingToFxqList(channelCode, handleDate);
            if (CollectionUtils.isNotEmpty(openAccountNotMappingToFxq)) {
                businessHandleService.updateSerialNoAndCheckErrorList("account_apply", openAccountNotMappingToFxq);
                checkList.addAll(openAccountNotMappingToFxq);
                flag = true;
            }
        }

        return flag;
    }

    /**
     * 交易申请数据头部校验
     * 1、校验产品是否是在售产品
     * 2、校验产品没有TA代码
     * 3、校验当前交易的账户是否已开户并是开户状态
     *
     * @param channelCode 渠道编码
     * @param handleDate  处理日期
     * @param stepCode  处理步骤
     * @param checkList   校验错误数据
     * @return
     */
    public boolean transApplyBusinessTopCheck(String channelCode, String handleDate, Integer stepCode,List<LinkedHashMap<String, Object>> checkList) {

        boolean flag = false;


        // 1、校验产品是否是在售产品
        List<LinkedHashMap<String, Object>> fundIsNotForChannel = transApplyMapper.fundIsNotForChannelList(channelCode, handleDate,stepCode);
        if (CollectionUtils.isNotEmpty(fundIsNotForChannel)) {
            businessHandleService.updateSerialNoAndCheckErrorList("trans_apply", fundIsNotForChannel);
            checkList.addAll(fundIsNotForChannel);
            flag = true;
        }

        // 2、校验产品没有TA代码
        List<LinkedHashMap<String, Object>> checkFundIsNotMappingToTa = transApplyMapper.checkFundIsNotMappingToTaList(channelCode, handleDate,stepCode);
        if (CollectionUtils.isNotEmpty(checkFundIsNotMappingToTa)) {
            businessHandleService.updateSerialNoAndCheckErrorList("trans_apply", checkFundIsNotMappingToTa);
            checkList.addAll(checkFundIsNotMappingToTa);
            flag = true;
        }

        // 3、校验当前交易的账户是否已开户并是开户状态
        List<LinkedHashMap<String, Object>> checkTransAccountIsNotValid = transApplyMapper.checkTransAccountIsNotValidList(channelCode, handleDate,stepCode);
        if (CollectionUtils.isNotEmpty(checkTransAccountIsNotValid)) {
            businessHandleService.updateSerialNoAndCheckErrorList("trans_apply", checkTransAccountIsNotValid);
            checkList.addAll(checkTransAccountIsNotValid);
            flag = true;
        }


        return flag;
    }

    /**
     * 4、校验当前产品的行情状态和当前交易业务是否相符
     * 5、校验认购交易、申购交易
     * 6、校验赎回交易
     * 7、校验收益级别收益率
     *
     * @param trans         交易数据
     * @param fundMarketMap 行情数据
     * @param checkList     校验结果
     * @return
     */
    public boolean transApplyBusinessCoreCheck(LinkedHashMap<String, Object> trans,
                                               Map<String, String> fundMarketMap,
                                               List<LinkedHashMap<String, Object>> checkList) {
        boolean flag = false;

        String channelCode = MapUtils.getString(trans, "CHANNELCODE");
        String transactionAccountId = MapUtils.getString(trans, "TRANSACTIONACCOUNTID");
        String fundCode = MapUtils.getString(trans, "FUNDCODE");
        String marketStatus = MapUtils.getString(fundMarketMap, fundCode);
        String businessCode = MapUtils.getString(trans, "BUSINESSCODE");


        boolean marketStatusFlag = checkFundMarketStatus(marketStatus, businessCode, trans, checkList);
        if (marketStatusFlag) {
            flag = true;
        }

        //1、查询产品
        FundInfo fundInfo = fundInfoMapper.selectFundInfoByChannelAndFundCode(channelCode, fundCode);
        if (fundInfo == null) {
            trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                    + MapUtils.getString(trans, "FUNDCODE") + "该产品相关信息不存在。");
            trans.put("CHECKSTATUS", 0);
            checkList.add(trans);
            return true;
        }

        //2、查询客户
        AccountStatus accountStatus = accountStatusMapper.selectAccountByChannelAndTransactionAccountId(channelCode, transactionAccountId);
        if (accountStatus == null) {
            trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "") +
                    "投资人基金交易账号为:" + transactionAccountId + "的客户没有成功的开户记录,不允许交易。");
            trans.put("CHECKSTATUS", 0);
            checkList.add(trans);
            return true;
        }

        if (ConstUtil.RG_TRANS.equals(businessCode) || ConstUtil.SG_TRANS.equals(businessCode)) {
            boolean transBuyCheckFlag = checkBuyTrans(fundInfo, accountStatus, trans, checkList);
            if (transBuyCheckFlag) {
                flag = true;
            }

            String fundType = fundInfo.getFundType();
            //某些产品类型需要校验受益级别和收益率  TODO
            if ("3".equals(fundType)) {//封闭收益类需要收益率
                boolean fundYieldCheckFlag = checkFundYield(trans, checkList);
                if (fundYieldCheckFlag) {
                    flag = true;
                }
            }

        }

        if (ConstUtil.SH_TRANS.equals(businessCode)) {
            boolean transRedeemCheckFlag = checkRedeemTrans(fundInfo, accountStatus, trans, checkList);
            if (transRedeemCheckFlag) {
                flag = true;
            }
        }


        return flag;
    }

    /**
     * 校验受益类别和收益率
     *
     * @param trans     交易信息
     * @param checkList 校验结果
     * @return
     */
    private boolean checkFundYield(LinkedHashMap<String, Object> trans,
                                   List<LinkedHashMap<String, Object>> checkList) {
        boolean flag = false;

        String channelCode = MapUtils.getString(trans, "CHANNELCODE");
        String transactionAccountId = MapUtils.getString(trans, "TRANSACTIONACCOUNTID");
        String fundCode = MapUtils.getString(trans, "FUNDCODE");
        BigDecimal applyAmount = new BigDecimal(MapUtils.getString(trans, "APPLICATIONAMOUNT", "0"));

        FundYield fundYield = fundYieldMapper.selectFundYield(channelCode, fundCode, applyAmount);
        if (fundYield == null) {
            trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "") +
                    "投资人基金交易账号为:" + transactionAccountId + "产品:" + fundCode + "购买金额:" + applyAmount + "没有对应的收益率,请检查。");
            trans.put("CHECKSTATUS", 0);
            checkList.add(trans);
            flag = true;
        }

        return flag;
    }

    /**
     * 校验认申购
     *
     * @param fundInfo      产品信息
     * @param accountStatus 账户信息
     * @param trans         交易数据
     * @param checkList     校验错误信息
     * @return
     */
    private boolean checkBuyTrans(FundInfo fundInfo, AccountStatus accountStatus, LinkedHashMap<String, Object> trans,
                                  List<LinkedHashMap<String, Object>> checkList) {
        boolean flag = false;
        /**
         * 1、查询产品,获得是否含费字段；获得是否允许个人机构购买标识；
         *  1.1、含费,根据金额查询含费信息；有从新计算购买金额起点
         *  1.2、不含费，以原始购买金额为准
         * 2、查询该客户的客户类型：0机构；1个人
         *  2.1、判断是否允许机构购买
         *  2.2、判断是否允许个人购买
         */
        String channelCode = MapUtils.getString(trans, "CHANNELCODE");
        String handleDate = MapUtils.getString(trans, "HANDLEDATE");
        String fundCode = MapUtils.getString(trans, "FUNDCODE");
        String transactionAccountId = MapUtils.getString(trans, "TRANSACTIONACCOUNTID");
        BigDecimal applyAmount = new BigDecimal(MapUtils.getString(trans, "APPLICATIONAMOUNT", "0"));
        String currencyType = MapUtils.getString(trans, "CURRENCYTYPE");
        String businessCode = MapUtils.getString(trans, "BUSINESSCODE");


        Integer transRateFlag = fundInfo.getTransRateFlag();//含费标识 0：不含；1：含
        Integer indiBuyFlag = fundInfo.getIndiBuyFlag();//允许个人购买标识 0：否；1：是
        Integer instBuyFlag = fundInfo.getInstBuyFlag();//允许机构购买标识 0：否；1：是

        String indiOrInst = accountStatus.getIndividualOrInstitution();//客户标识 0：机构；1：个人；2：产品

        if (("1".equals(indiOrInst) && 0 == indiBuyFlag) || //客户为个人但是个人购买标识为否
                ("0".equals(indiOrInst) && 0 == instBuyFlag) //客户为机构但是机构购买标识为否
        ) {
            trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "") +
                    "投资人基金交易账号为:" + transactionAccountId + "客户类型与产品购买限制(个人/机构)不符,不允许交易。");
            trans.put("CHECKSTATUS", 0);
            checkList.add(trans);
            return true;
        }

        if (1 == transRateFlag) {//含费
            //查询费率
            FundTransRate fundTransRate = fundTransRateMapper.selectFundTransRateByBuyAmount(channelCode, fundCode, businessCode, indiOrInst, currencyType, applyAmount);
            if (fundTransRate == null) {
                trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "") +
                        "产品:" + fundCode + "为含费产品,客户类型:" + indiOrInst + "币种:" + currencyType + "交易类型" + businessCode + "没有配置交易费率,请检查。");
                trans.put("CHECKSTATUS", 0);
                checkList.add(trans);
                return true;
            }
            applyAmount = applyAmount.divide(fundTransRate.getTransRate().add(new BigDecimal(1)), 2, BigDecimal.ROUND_DOWN).stripTrailingZeros();
        }

        //查询是否是首次 TODO  默认都是首次购买，后期完善;首次购买多笔也是按首次校验存在问题;
        int transContractCount = 0;

        //定义最高最低，级差等变量
        BigDecimal buyMin = null;
        BigDecimal buyMax = null;
        BigDecimal buyRange = null;
        //追加最低金额
        BigDecimal appendMin = null;
        BigDecimal appendMax = null;
        BigDecimal appendRange = null;

        /**
         * 分情况赋值
         * 1、个人首次
         * 2、个人追加
         * 3、机构首次
         * 4、机构追加
         */
        String msg = "";
        if (transContractCount == 0 && "1".equals(indiOrInst)) {
            msg = "个人首次";
            buyMin = fundInfo.getIndiFirstMinAmount();
            buyMax = fundInfo.getIndiFirstMaxAmount();
            buyRange = fundInfo.getIndiFirstRangeAmount();
        }

        if (transContractCount != 0 && "1".equals(indiOrInst)) {
            msg = "个人追加";
            buyMin = fundInfo.getIndiAppendMinAmount();
            buyMax = fundInfo.getIndiAppendMaxAmount();
            buyRange = fundInfo.getIndiAppendRangeAmount();

            appendMin = fundInfo.getIndiAppendMinAmount();
            appendMax = fundInfo.getIndiAppendMaxAmount();
            appendRange = fundInfo.getIndiAppendRangeAmount();
        }

        if (transContractCount == 0 && "0".equals(indiOrInst)) {
            msg = "机构首次";
            buyMin = fundInfo.getInstFirstMinAmount();
            buyMax = fundInfo.getInstFirstMaxAmount();
            buyRange = fundInfo.getInstFirstRangeAmount();
        }

        if (transContractCount != 0 && "0".equals(indiOrInst)) {
            msg = "机构追加";
            buyMin = fundInfo.getInstAppendMinAmount();
            buyMax = fundInfo.getInstAppendMaxAmount();
            buyRange = fundInfo.getInstAppendRangeAmount();

            appendMin = fundInfo.getInstAppendMinAmount();
            appendMax = fundInfo.getInstAppendMaxAmount();
            appendRange = fundInfo.getInstAppendRangeAmount();
        }

        //判断金额不小于最小，不大于最大，与级差的余数为0;
        if (applyAmount.compareTo(buyMin) < 0) {

            /**如果是首次购买小于最小金额,那有可能买了多笔,但是是追加的那笔按首次购买最忌金额判断的,需要再次判断
             * 1、当前交易判断为首次并且满足不小于追加购买最低金额,不大于追加购买最高金额,满足追加购买极差
             * 2、如果查询出该产品该客户还有一笔比当前金额大的并且满足首次购买最低金额的交易，则该笔交易按成功处理
             */
            if(transContractCount == 0
                    && applyAmount.compareTo(appendMin) >= 0
                    && applyAmount.compareTo(appendMax) <= 0
                    && applyAmount.divideAndRemainder(appendRange)[1].compareTo(BigDecimal.ZERO) == 0){

                int count = transApplyMapper.selectManyBuyTransSecondCheck(channelCode,handleDate,fundCode,transactionAccountId,businessCode,applyAmount);
                if(count == 0){
                    trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                            + applyAmount + "该金额不满足" + msg + "最低金额" + buyMin);
                    trans.put("CHECKSTATUS", 0);
                    checkList.add(trans);
                    flag = true;
                }
            }else {
                trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                        + applyAmount + "该金额不满足" + msg + "最低金额" + buyMin);
                trans.put("CHECKSTATUS", 0);
                checkList.add(trans);
                flag = true;
            }
        }

        if (applyAmount.compareTo(buyMax) > 0) {
            trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                    + applyAmount + "该金额不满足" + msg + "最高金额" + buyMax);
            trans.put("CHECKSTATUS", 0);
            checkList.add(trans);
            flag = true;
        }

        if (applyAmount.divideAndRemainder(buyRange)[1].compareTo(BigDecimal.ZERO) != 0) {
            trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                    + applyAmount + "该金额不满足" + msg + "级差金额" + buyRange);
            trans.put("CHECKSTATUS", 0);
            checkList.add(trans);
            flag = true;
        }

        return flag;

    }

    /**
     * 校验赎回
     *
     * @param fundInfo      产品信息
     * @param accountStatus 账户信息
     * @param trans         交易数据
     * @param checkList     校验错误信息
     * @return
     */
    private boolean checkRedeemTrans(FundInfo fundInfo, AccountStatus accountStatus, LinkedHashMap<String, Object> trans,
                                     List<LinkedHashMap<String, Object>> checkList) {
        boolean flag = false;

        String channelCode = MapUtils.getString(trans, "CHANNELCODE");
        String handleDate = MapUtils.getString(trans, "HANDLEDATE");
        String fundCode = MapUtils.getString(trans, "FUNDCODE");
        String transactionAccountId = MapUtils.getString(trans, "TRANSACTIONACCOUNTID");
        String indiOrInst = accountStatus.getIndividualOrInstitution();//客户标识 0：机构；1：个人；2：产品
        BigDecimal applyVol = new BigDecimal(MapUtils.getString(trans, "APPLICATIONVOL", "0"));

        /**
         * 校验赎回
         * 1、个人赎回最低、最高、级差
         * 2、机构赎回最低、最高、级差
         */
        //定义最高最低，级差,最低持有份额等变量
        BigDecimal redeemMin = null;
        BigDecimal redeemRange = null;
        BigDecimal bookMin = null;
        //查询该客户该产品的持仓份额
        BigDecimal remainShares = new BigDecimal(0);

        String msg = "";
        if ("0".equals(indiOrInst)) {//机构
            msg = "机构赎回";
            redeemMin = fundInfo.getInstRedeemMinVol();
            redeemRange = fundInfo.getInstRedeemRangeVol();
            bookMin = fundInfo.getInstBookMinVol();
        } else if ("1".equals(indiOrInst)) {//个人
            msg = "个人赎回";
            redeemMin = fundInfo.getIndiRedeemMinVol();
            redeemRange = fundInfo.getIndiRedeemRangeVol();
            bookMin = fundInfo.getIndiBookMinVol();
        }

        //校验是否全额
        if (applyVol.compareTo(remainShares) == 0) {
            LOGGER.info("客户:{},产品:{},全额赎回:{}", transactionAccountId, fundInfo.getFundCode(), applyVol);
            return false;
        }else{
            //判断该客户是否有多笔赎回合计满足全额赎回
            BigDecimal sumVol = transApplyMapper.selectManyRedeemTransSumVol(channelCode,handleDate,fundCode,transactionAccountId,remainShares);
            if(remainShares.compareTo(sumVol) == 0){
                return false;
            }
        }

        //最低赎回份额
        if (applyVol.compareTo(redeemMin) < 0) {
            trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                    + applyVol + "该份额不满足" + msg + "最低份额" + redeemMin);
            trans.put("CHECKSTATUS", 0);
            checkList.add(trans);
            flag = true;
        }

        //赎回级差
        if (applyVol.divideAndRemainder(redeemRange)[1].compareTo(BigDecimal.ZERO) != 0) {
            trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                    + applyVol + "该份额不满足" + msg + "级差份额" + redeemRange);
            trans.put("CHECKSTATUS", 0);
            checkList.add(trans);
            flag = true;
        }

        //最低持仓份额
        if ((remainShares.subtract(applyVol)).compareTo(bookMin) < 0) {
            trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                    + "持仓份额:" + remainShares + "减去赎回份额:" + applyVol + ",不满足"
                    + msg + "最低账面份额" + bookMin);
            trans.put("CHECKSTATUS", 0);
            checkList.add(trans);
            flag = true;
        }

        return flag;
    }

    /**
     * 校验行情状态
     *
     * @param marketStatus 行情状态
     * @param businessCode 业务编码
     * @param trans        交易数据
     * @param checkList    校验错误信息
     * @return
     */
    private boolean checkFundMarketStatus(String marketStatus, String businessCode,
                                          LinkedHashMap<String, Object> trans,
                                          List<LinkedHashMap<String, Object>> checkList) {
        boolean flag = false;

        if (StringUtils.isEmpty(marketStatus)) {
            trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "") + MapUtils.getString(trans, "FUNDCODE") + "该产品没有行情数据。");
            trans.put("CHECKSTATUS", 0);
            checkList.add(trans);
            flag = true;

        } else {
            /**
             * 020 ->1
             * 022 ->0,6
             * 024 ->0,5
             */
            switch (businessCode) {
                case ConstUtil.RG_TRANS:
                    if (!"1".equals(marketStatus)) {
                        trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                                + MapUtils.getString(trans, "FUNDCODE") + "的交易为认购,行情不是发行状态。");
                        trans.put("CHECKSTATUS", 0);
                        checkList.add(trans);
                        flag = true;
                    }
                    break;
                case ConstUtil.SG_TRANS:
                    if (!("0".equals(marketStatus) || "6".equals(marketStatus))) {
                        trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                                + MapUtils.getString(trans, "FUNDCODE") + "的交易为申购,行情不是可申赎/可申购状态。");
                        trans.put("CHECKSTATUS", 0);
                        checkList.add(trans);
                        flag = true;
                    }
                    break;
                case ConstUtil.SH_TRANS:
                    if (!("0".equals(marketStatus) || "5".equals(marketStatus))) {
                        trans.put("ERRORMSG", MapUtils.getString(trans, "ERRORMSG", "")
                                + MapUtils.getString(trans, "FUNDCODE") + "的交易为赎回,行情不是可申赎/可赎回状态。");
                        trans.put("CHECKSTATUS", 0);
                        checkList.add(trans);
                        flag = true;
                    }
                    break;
            }

        }
        return flag;
    }

    /**
     * 重新分配TA代码,某些产品类型有合同号限制
     * @param channelCode 渠道编码
     * @param handleDate 处理日期
     * @param checkList 校验错误集合
     */

    public void  reloadAllocationTaFundCode(String channelCode, String handleDate, Integer stepCode,
                                            List<LinkedHashMap<String, Object>> checkList) {


        //校验完直接更新,只更新失败的

    }
}
